package com.zyk.scaffold.tcp.listener;

import com.zyk.scaffold.tcp.listener.event.SessionEvent;
import com.zyk.scaffold.tcp.session.Session;
import com.zyk.scaffold.tcp.session.SessionManager;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


@Slf4j
public class HeartbeatListener implements Runnable, SessionListener{

    private volatile boolean stop = false;
    private int checkPeriod = 30 * 1000;
    ReentrantLock lock = new ReentrantLock();
    Condition notEmpty = lock.newCondition();

    private SessionManager sessionManager;

    public HeartbeatListener(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
        sessionManager.registSessionListener(this);
    }

    @Override
    public void run() {
        while (!stop){
            if (isEmpty()) {
                await();
            }
            try {
                Thread.sleep(checkPeriod);
            } catch (InterruptedException e) {
                log.error("TcpHeartbeatListener run occur InterruptedException!", e);
            }
            if (stop) {
                break;
            }
            checkHeartBeat();
        }

    }

    @Override
    public void sessionCreated(SessionEvent event){
        signal();
    }

    private void signal() {
        boolean isLock = false;
        try {
            isLock = lock.tryLock(100, TimeUnit.MILLISECONDS);
            if (isLock){
                notEmpty.signalAll();
            }
        } catch (InterruptedException e) {
            log.error("HeartbeatListener signal InterruptedException!", e);
        } catch (Exception e) {
            log.error("signal Thread Queue error!", e);
        } finally {
            if (isLock){
                lock.unlock();
            }
        }
    }

    private void await() {
        boolean isLock = lock.tryLock();
        if (isLock) {
            try {
                notEmpty.await();
            } catch (InterruptedException e) {
                log.error("HeartbeatListener await InterruptedException!", e);
            } catch (Exception e) {
                log.error("await Thread Queue error!", e);
            } finally {
                lock.unlock();
            }
        }
    }

    public void checkHeartBeat() {
        Session[] sessions = sessionManager.getSessions();
        LocalDateTime target = LocalDateTime.now().minus(sessionManager.getSessionConfig().getExpireTime(), ChronoUnit.MILLIS);
        for (Session session : sessions) {
            if (session.beforAccessed(target)) {
                session.distory();
                log.info("heart is expire,clear sessionId:" + session.getId());
            }
        }
    }


    private boolean isEmpty() {
        return sessionManager.getSessionCount() == 0;
    }


    public void stop() {
        this.stop = true;
    }
}
